package com.linewell.encryption.utils.sm2;


import com.linewell.encryption.utils.Base64Utils;
import com.linewell.encryption.utils.sm2.vo.SM2Result;
import com.linewell.encryption.utils.sm3.SM3Digest;
import org.bouncycastle.asn1.*;
import org.bouncycastle.math.ec.ECPoint;

import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.Enumeration;

/**
 * 国密算法的签名、验签
 *
 * @author wucan
 */
public class SM2VerifySignUtils {
    /**
     * 默认USERID
     */
    private static String USER_ID = "1234567812345678";

    /**
     * 私钥签名
     * 使用SM3进行对明文数据计算一个摘要值
     *
     * @param privatekey 私钥
     * @param sourceData 明文数据
     * @return 签名后的值
     * @throws Exception 异常
     */
    public static String sign2SM2(byte[] privatekey, byte[] sourceData) throws Exception {
        SM2Factory factory = SM2Factory.getInstance();
        BigInteger userD = new BigInteger(privatekey);
        ECPoint userKey = factory.ecc_point_g.multiply(userD);
        SM3Digest sm3Digest = new SM3Digest();
        byte[] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
        sm3Digest.update(z, 0, z.length);
        sm3Digest.update(sourceData, 0, sourceData.length);
        ASN1EncodableVector v2 = new ASN1EncodableVector();

        byte[] md = new byte[32];
        sm3Digest.doFinal(md, 0);
        SM2Result sm2Result = new SM2Result();
        factory.sm2Sign(md, userD, userKey, sm2Result);
        ASN1Integer dr = new ASN1Integer(sm2Result.r);
        ASN1Integer ds = new ASN1Integer(sm2Result.s);
        v2.add(dr);
        v2.add(ds);
        DERSequence sign = new DERSequence(v2);
        return Base64Utils.byteToBase64(sign.getEncoded());
    }

    /**
     * 验证签名
     *
     * @param publicKey  公钥信息
     * @param sourceData 密文信息
     * @param signData   签名信息
     * @return 验签的对象 包含了相关参数和验签结果
     */
    public static Boolean verifySignSM2(byte[] publicKey, byte[] sourceData, byte[] signData) {
        try {
            byte[] formatedPubKey;
            if (publicKey.length == 64) {
                // 添加一字节标识，用于ECPoint解析
                formatedPubKey = new byte[65];
                formatedPubKey[0] = 0x04;
                System.arraycopy(publicKey, 0, formatedPubKey, 1, publicKey.length);
            } else {
                formatedPubKey = publicKey;
            }
            SM2Factory factory = SM2Factory.getInstance();
            ECPoint userKey = factory.ecc_curve.decodePoint(formatedPubKey);

            SM3Digest sm3Digest = new SM3Digest();
            byte[] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
            sm3Digest.update(z, 0, z.length);
            sm3Digest.update(sourceData, 0, sourceData.length);
            byte[] md = new byte[32];
            sm3Digest.doFinal(md, 0);
            ByteArrayInputStream bis = new ByteArrayInputStream(signData);
            ASN1InputStream dis = new ASN1InputStream(bis);
            SM2Result sm2Result;
            ASN1Primitive derObj = dis.readObject();
            Enumeration e = ((ASN1Sequence) derObj).getObjects();
            BigInteger r = ((ASN1Integer) e.nextElement()).getValue();
            BigInteger s = ((ASN1Integer) e.nextElement()).getValue();
            sm2Result = new SM2Result();
            sm2Result.r = r;
            sm2Result.s = s;
            factory.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);
            return sm2Result.r.equals(sm2Result.R);
        } catch (IllegalArgumentException e) {
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
